package com.googlecode.gaal.analysis.impl;

import java.util.Iterator;

import com.googlecode.gaal.analysis.api.Context;
import com.googlecode.gaal.analysis.api.Filter;
import com.googlecode.gaal.analysis.api.IntervalSetBuilder;
import com.googlecode.gaal.data.api.IntSequence;
import com.googlecode.gaal.data.api.IntervalSet;
import com.googlecode.gaal.data.api.Multiset;
import com.googlecode.gaal.data.api.SymbolTable;
import com.googlecode.gaal.suffix.api.EmbeddedSuffixTree;
import com.googlecode.gaal.suffix.api.EmbeddedSuffixTree.EmbeddedInterval;
import com.googlecode.gaal.suffix.api.IntervalTree.Interval;
import com.googlecode.gaal.suffix.api.SuffixArray;
import com.googlecode.gaal.suffix.impl.EmbeddedSuffixTreeImpl;

public class EmbeddedIntervalExtractor {
    private final SuffixArray sa;
    private final SymbolTable<?> symbolTable;
    private final IntervalSetBuilder intervalSetBuilder;
    private final Filter<EmbeddedInterval> contextFilter;
    private final int windowSize;

    public EmbeddedIntervalExtractor(SuffixArray sa, SymbolTable<?> symbolTable, IntervalSetBuilder intervalSetBuilder,
            Filter<EmbeddedInterval> contextFilter, int windowSize) {
        this.sa = sa;
        this.symbolTable = symbolTable;
        this.intervalSetBuilder = intervalSetBuilder;
        this.contextFilter = contextFilter;
        this.windowSize = windowSize;
    }

    public Iterator<EmbeddedInterval> iterator(Interval interval) {
        return new EmbeddedIntervalIterator(interval);
    }

    private class EmbeddedIntervalIterator implements Iterator<EmbeddedInterval> {
        private Iterator<EmbeddedInterval> embeddedIterator;
        private EmbeddedInterval next;

        private EmbeddedIntervalIterator(Interval interval) {
            EmbeddedSuffixTree est = EmbeddedSuffixTreeImpl.create(sa, interval, windowSize, symbolTable);
            if (est != null) {
                IntervalSet<EmbeddedInterval> embeddedIntervalSet = intervalSetBuilder.buildIntervalSet(est);
                embeddedIterator = embeddedIntervalSet.iterator();
                next = advance();
            }
        }

        @Override
        public boolean hasNext() {
            return next != null;
        }

        @Override
        public EmbeddedInterval next() {
            EmbeddedInterval context = next;
            next = advance();
            return context;
        }

        private EmbeddedInterval advance() {
            while (embeddedIterator.hasNext()) {
                EmbeddedInterval next = embeddedIterator.next();
                if (contextFilter == null || contextFilter.passed(next)) {
                    return next;
                }
            }
            return null;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    };

    public static class EmbeddedContext implements Context {

        private final EmbeddedInterval embeddedInterval;

        protected EmbeddedContext(EmbeddedInterval embeddedInterval) {
            this.embeddedInterval = embeddedInterval;
        }

        @Override
        public IntSequence leftSequence() {
            return embeddedInterval.getEmbeddingInterval().label();
        }

        @Override
        public IntSequence rightSequence() {
            return embeddedInterval.label();
        }

        @Override
        public Multiset<IntSequence> fillerSet() {
            return embeddedInterval.fillerSet();
        }

        @Override
        public int fillerSetSize() {
            return embeddedInterval.size();
        }
    }
}
